import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LexicalAnalyzer {

    public ArrayList<String> keyWords = new ArrayList<>(Arrays.asList("int","float", "char", "void", "return", "const", "main", "struct", "union", "switch", "case", "default", "break")); //全部关键字***
    public ArrayList<String> numberMap = new ArrayList<>(Arrays.asList("int", "float", "char","void", "return", "const", "main",
            "+", "-", "*", "/", "%", "=", ">", "<",
            "==", "<=", ">=", "!=", "&&", "||",
            "(", ")", "{", "}", ";", ",", "struct", "union", ":", "switch", "case", "default", "break"));  //所有要考虑的符号，输出时也可以利用List的下标进行三元组的生成***
    public List<String> tokens = new ArrayList<>();

    // 词法分析
    public List<String> analyze(String str) throws LexicalAnalyzeException{  //依据确定化和最小化的有限自动机的逻辑进行词法分析
        int len = str.length();
        int nowLine = 1;  //当前行数
        int i = 0;
        while (i < len) {
            char ch = str.charAt(i);  //当前字符
            String s = "" + ch;
            if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { // 是空格 \t \r \n就跳过
                ++i;
                if (ch == '\n') {  // \n就换行，在windows中换行是\r\n，所以只检查\n即可
                    ++nowLine;
                }
            } else if (Character.isLetter(ch)) { // 字母开头只能是关键字或标识符
                ch = str.charAt(++i);
                while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '_') {  //数字字母下划线就接着加
                    s += ch;
                    ch = str.charAt(++i);
                }
                if (keyWords.contains(s)) {
                    transform(s, "KW");
                } else {
                    transform(s, "IDN");
                }
            } else if (Character.isDigit(ch)) { // 数字开头只能是整数或浮点数
                ch = str.charAt(++i);
                while (Character.isDigit(ch)) {
                    s += ch;
                    ch = str.charAt(++i);
                }
                if (ch == '.') {  //浮点数
                    ch = str.charAt(++i);
                    while (Character.isDigit(ch)) {
                        s += ch;
                        ++i;
                    }
                    transform(s, "FLOAT");
                } else {
                    transform(s, "INT");
                }
            } else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%') { // + - * / %
                ++i;
                transform("" + ch, "OP");
            } else if (ch == '=' || ch == '<' || ch == '>') { // = < >
                ch = str.charAt(++i);
                if (ch == '=') {  // == <= >=
                    s += ch;
                    ++i;
                }
                transform(s, "OP");
            } else if (ch == '!') { // !
                ch = str.charAt(++i);
                if (ch == '=') { // !=
                    s += ch;
                    ++i;
                    transform(s, "OP");
                } else {  //!后面除了=之外只能什么都没有，否则一定是错的
                    throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
                }
            } else if (ch == '&' || ch == '|') { // & |
                ch = str.charAt(++i);
                if (ch == s.charAt(0)) { //& ｜后面除了自身之外只能什么都没有，否则一定是错的
                    s += ch;
                    ++i;
                    transform(s, "OP");
                } else {
                    throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
                }
            } else if (ch == '(' || ch == ')' || ch == '{' || ch == '}' || ch == ';' || ch == ',') { // ( ) { } ; ,
                ++i;
                transform(s, "SE");  //界符
            } else if (ch == '\"') { // "
                ch = str.charAt(++i);
                while (ch != '"') {
                    s += ch;
                    if (ch == '\\') { //当接收到一个\符号时，下一个即使是"也要继续循环，因为\在这里表示转义
                        s += str.charAt(++i);
                    }
                    ch = str.charAt(++i);
                }
                ++i; // 跳过右引号
                transform(s.substring(1), "STRING");
            } else if (ch == '\'') { // '
                ch = str.charAt(++i);
                if (ch == '\'') {  //连着两个单引号直接报错
                    throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
                } else if (ch == '\\') { //若为\则还能接受一个字符，因为\表示转义
                    s += ch;
                    ch = str.charAt(++i);
                    s += ch;
                } else {
                    s += ch;
                }
                ch = str.charAt(++i);
                ++i; // 跳过右引号
                if (ch == '\'') {  //正常的一个字符
                    transform(s.substring(1), "CHAR");
                } else {
                    throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
                }
            } else if (ch == ':') { // '
                ch = str.charAt(++i);
                while(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
                    if (ch == '\n') {  // \n就换行，在windows中换行是\r\n，所以只检查\n即可
                        ++nowLine;
                    }
                	ch = str.charAt(++i);
                }
                if (ch == '{') { // :{ 后边只能是左中括号
                    transform(s, "OP");
                    s = "" + ch;
                    transform(s, "SE");
                    ++i;
                } else {  //!后面除了=之外只能什么都没有，否则一定是错的
                    throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
                }
            } else {
                throw new LexicalAnalyzeException("词法分析异常: 第" + nowLine + "行");
            }
        }
        System.out.println("----- 词法分析结果 -----");
        for (String token : tokens) {
            System.out.println(token);
        }
        System.out.println();
        return tokens;
    }

    // 将词法分析的结果转化为token序列
    public void transform(String result, String type) {
        String token;
        if (numberMap.contains(result)) {  //如果是关键字、运算符、界符等已经定义好的符号
            token = result + "\t<" + type + "," + (numberMap.indexOf(result) + 1) + ">";
        } else {
            token = result + "\t<" + type + "," + result + ">";  //标识符
        }
        tokens.add(token);  //词法分析的结果
    }

    //将tokens序列写到文件里
    public void writeTokens(String filePath){
        String content = "";
        for (String token : tokens) {
            content += token + "\n";
        }
        try {
            Files.write(Paths.get(filePath), content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

